home *** CD-ROM | disk | FTP | other *** search
-
- {**************************************************}
- { }
- { Turbo Pascal for Windows }
- { Object Linking and Embedding demo program }
- { }
- { Copyright (c) 1992 by Borland International }
- { }
- {**************************************************}
-
- program OleClnt;
-
- { This program demonstrates how to implement an OLE client application.
- The program uses the new Ole, ShellAPI, and CommDlg units, and requires
- that the OLECLI.DLL, SHELL.DLL, and COMMDLG.DLL libraries are present.
- The program allows you to create embedded and linked objects using the
- Edit|Paste and Edit|Paste link commands. The OLE objects can be moved
- and resized, and they can be activated through double clicks or using
- the Edit|Object menu. Workspaces can be saved and loaded using the
- File menu. }
-
- uses Strings, WinTypes, WinProcs, WObjects, Ole, ShellAPI, CommDlg;
-
- {$R OLECLNT}
-
- const
-
- { Resource IDs }
-
- id_Menu = 100;
- id_About = 100;
-
- { Menu command IDs }
-
- cm_FileNew = 100;
- cm_FileOpen = 101;
- cm_FileSave = 102;
- cm_FileSaveAs = 103;
- cm_FileExit = 104;
- cm_EditCut = 200;
- cm_EditCopy = 201;
- cm_EditPaste = 202;
- cm_EditPasteLink = 203;
- cm_EditClear = 204;
- cm_HelpAbout = 300;
- cm_VerbMin = 900;
- cm_VerbMax = 999;
-
- { Menu item positions }
-
- pos_Edit = 1; { Position of Edit item on main menu }
- pos_Object = 6; { Position of Object item on Edit menu }
-
- type
-
- { Pointer types }
-
- PAppClient = ^TAppClient;
- PAppStream = ^TAppStream;
- PObjectWindow = ^TObjectWindow;
- PMainWindow = ^TMainWindow;
-
- { Filename string }
-
- TFilename = array[0..255] of Char;
-
- { OLE file header }
-
- TOleFileHeader = array[1..4] of Char;
-
- { Application client structure }
-
- TAppClient = record
- OleClient: TOleClient;
- ObjectWindow: PObjectWindow;
- end;
-
- { Application stream structure }
-
- TAppStream = record
- OleStream: TOleStream;
- OwlStream: PStream;
- end;
-
- { OLE object window }
-
- TObjectWindow = object(TWindow)
- AppClient: TAppClient;
- OleObject: POleObject;
- Framed: Boolean;
- constructor Init(Link: Boolean);
- constructor Load(var S: TStream);
- destructor Done; virtual;
- function GetClassName: PChar; virtual;
- procedure GetWindowClass(var AWndClass: TWndClass); virtual;
- procedure SetupWindow; virtual;
- procedure Store(var S: TStream); virtual;
- function CanClose: Boolean; virtual;
- procedure Paint(PaintDC: HDC; var PaintInfo: TPaintStruct); virtual;
- procedure Check(OleStatus: TOleStatus);
- procedure GetObjectClass(ClassName: PChar);
- function IsLinked: Boolean;
- procedure Update;
- procedure OpenObject(Verb: Word);
- procedure CloseObject;
- procedure CopyToClipboard;
- procedure Delete;
- procedure Changed;
- procedure BringToFront;
- procedure GetBounds(var R: TRect);
- procedure SetBounds(var R: TRect);
- procedure ShowFrame(EnableFrame: Boolean);
- procedure WMGetMinMaxInfo(var Msg: TMessage);
- virtual wm_First + wm_GetMinMaxInfo;
- procedure WMMove(var Msg: TMessage);
- virtual wm_First + wm_Move;
- procedure WMSize(var Msg: TMessage);
- virtual wm_First + wm_Size;
- procedure WMLButtonDown(var Msg: TMessage);
- virtual wm_First + wm_LButtonDown;
- procedure WMMouseMove(var Msg: TMessage);
- virtual wm_First + wm_MouseMove;
- procedure WMLButtonUp(var Msg: TMessage);
- virtual wm_First + wm_LButtonUp;
- procedure WMLButtonDblClk(var Msg: TMessage);
- virtual wm_First + wm_LButtonDblClk;
- end;
-
- { Application main window }
-
- TMainWindow = object(TWindow)
- ObjectWindow: PObjectWindow;
- ClientDoc: LHClientDoc;
- Modified: Boolean;
- Filename: TFilename;
- constructor Init;
- destructor Done; virtual;
- function CanClose: Boolean; virtual;
- procedure InitDocument;
- procedure DoneDocument;
- procedure UpdateDocument;
- procedure SetFilename(Name: PChar);
- function NewFile(Name: PChar): Boolean;
- function LoadFile: Boolean;
- function SaveFile: Boolean;
- function Save: Boolean;
- function SaveAs: Boolean;
- procedure NewObjectWindow(Link: Boolean);
- procedure SelectWindow(Window: PObjectWindow);
- procedure UpdateObjectMenu;
- procedure WMLButtonDown(var Msg: TMessage);
- virtual wm_First + wm_LButtonDown;
- procedure WMInitMenu(var Msg: TMessage);
- virtual wm_First + wm_InitMenu;
- procedure CMFileNew(var Msg: TMessage);
- virtual cm_First + cm_FileNew;
- procedure CMFileOpen(var Msg: TMessage);
- virtual cm_First + cm_FileOpen;
- procedure CMFileSave(var Msg: TMessage);
- virtual cm_First + cm_FileSave;
- procedure CMFileSaveAs(var Msg: TMessage);
- virtual cm_First + cm_FileSaveAs;
- procedure CMFileExit(var Msg: TMessage);
- virtual cm_First + cm_FileExit;
- procedure CMEditCut(var Msg: TMessage);
- virtual cm_First + cm_EditCut;
- procedure CMEditCopy(var Msg: TMessage);
- virtual cm_First + cm_EditCopy;
- procedure CMEditPaste(var Msg: TMessage);
- virtual cm_First + cm_EditPaste;
- procedure CMEditPasteLink(var Msg: TMessage);
- virtual cm_First + cm_EditPasteLink;
- procedure CMEditClear(var Msg: TMessage);
- virtual cm_First + cm_EditClear;
- procedure CMHelpAbout(var Msg: TMessage);
- virtual cm_First + cm_HelpAbout;
- procedure DefCommandProc(var Msg: TMessage); virtual;
- end;
-
- { Application object }
-
- TApp = object(TApplication)
- constructor Init(AName: PChar);
- destructor Done; virtual;
- procedure InitMainWindow; virtual;
- end;
-
- { Initialized globals }
-
- const
- Dragging: Boolean = False;
- OleFileHeader: TOleFileHeader = 'TPOF';
- OleProtocol: PChar = 'StdFileEditing';
- OleObjectName: PChar = 'Object';
- OleClntTitle: PChar = 'OLE Client Demo';
-
- { Global variables }
-
- var
- App: TApp;
- DragPoint: TPoint;
- MainWindow: PMainWindow;
- OleClientVTbl: TOleClientVTbl;
- OleStreamVTbl: TOleStreamVTbl;
- PixPerInch: TPoint;
- CFObjectLink, CFOwnerLink: Word;
-
- { TObjectWindow stream registration record }
-
- const
- RObjectWindow: TStreamRec = (
- ObjType: 999;
- VmtLink: Ofs(TypeOf(TObjectWindow)^);
- Load: @TObjectWindow.Load;
- Store: @TObjectWindow.Store);
-
- { Display a message using the MessageBox API routine. }
-
- function Message(S: PChar; Flags: Word): Word;
- begin
- Message := MessageBox(MainWindow^.HWindow, S, OleClntTitle, Flags);
- end;
-
- { Display an error message. }
-
- procedure Error(ErrorStr, ErrorArg: PChar);
- var
- S: array[0..255] of Char;
- begin
- wvsprintf(S, ErrorStr, ErrorArg);
- Message(S, mb_IconExclamation + mb_Ok);
- end;
-
- { Display OLE operation error message. }
-
- procedure OleError(Status: Word);
- var
- S: array[0..7] of Char;
- begin
- wvsprintf(S, '%d', Status);
- Error('Warning: OLE operation failed, error code = %s.', S);
- end;
-
- { Display an Open or Save As file dialog using the Common Dialog DLL. }
-
- function FileDialog(Owner: HWnd; Filename: PChar; Save: Boolean): Boolean;
- const
- DefOpenFilename: TOpenFilename = (
- lStructSize: SizeOf(TOpenFilename);
- hwndOwner: 0;
- hInstance: 0;
- lpstrFilter: 'OLE files (*.OLE)'#0'*.ole'#0;
- lpstrCustomFilter: nil;
- nMaxCustFilter: 0;
- nFilterIndex: 0;
- lpstrFile: nil;
- nMaxFile: SizeOf(TFilename);
- lpstrFileTitle: nil;
- nMaxFileTitle: 0;
- lpstrInitialDir: nil;
- lpstrTitle: nil;
- Flags: 0;
- nFileOffset: 0;
- nFileExtension: 0;
- lpstrDefExt: 'ole');
- var
- OpenFilename: TOpenFilename;
- begin
- OpenFilename := DefOpenFilename;
- OpenFilename.hwndOwner := Owner;
- OpenFilename.lpstrFile := Filename;
- if Save then
- begin
- OpenFilename.Flags := ofn_PathMustExist + ofn_NoChangeDir +
- ofn_OverwritePrompt;
- FileDialog := GetSaveFilename(OpenFilename);
- end else
- begin
- OpenFileName.Flags := ofn_PathMustExist + ofn_HideReadOnly;
- FileDialog := GetOpenFilename(OpenFilename);
- end;
- end;
-
- { OLE client callback routine. Called by the OLE client library to notify
- the application of any changes to an object. In this application, the
- Client parameter is always a PAppClient, so a typecast can be used to
- find the corresponding TObjectWindow. The OLE object window's Changed
- method is called whenever the contained OLE object is changed, saved,
- or renamed. The callback routine returns 1 to satisfy ole_Query_Paint
- and ole_Query_Retry notifications. }
-
- function ClientCallBack(Client: POleClient; Notification:
- TOle_Notification; OleObject: POleObject): Integer; export;
- begin
- ClientCallBack := 1;
- case Notification of
- ole_Changed, ole_Saved, ole_Renamed:
- PAppClient(Client)^.ObjectWindow^.Changed;
- end;
- end;
-
- { Selector increment. This is not a true procedure. Instead, it is an
- external symbol whose offset represents the value to add to a selector
- to increment a pointer by 64K bytes. }
-
- procedure AHIncr; far; external 'KERNEL' index 114;
-
- { Read or write to or from a stream. This function supports transfers of
- blocks larger than 64K bytes. It guards against segment overruns, and
- transfers data in blocks of up to 32K bytes. }
-
- function StreamInOut(var S: TStream; Buffer: Pointer; Size: Longint;
- Writing: Boolean): Longint;
- var
- N: Longint;
- begin
- StreamInOut := Size;
- while Size <> 0 do
- begin
- N := $10000 - PtrRec(Buffer).Ofs;
- if N > $8000 then N := $8000;
- if N > Size then N := Size;
- if Writing then S.Write(Buffer^, N) else S.Read(Buffer^, N);
- Inc(PtrRec(Buffer).Ofs, N);
- if PtrRec(Buffer).Ofs = 0 then Inc(PtrRec(Buffer).Seg, Ofs(AHIncr));
- Dec(Size, N);
- end;
- if S.Status <> 0 then StreamInOut := 0;
- end;
-
- { OLE stream read callback function. In this application, the Stream
- parameter is always a PAppStream, so a typecast can be used to find the
- corresponding ObjectWindows stream. }
-
- function StreamGet(Stream: POleStream; Buffer: PChar;
- Size: LongInt): LongInt; export;
- begin
- StreamGet := StreamInOut(PAppStream(Stream)^.OwlStream^,
- Buffer, Size, False);
- end;
-
- { OLE stream write callback function. In this application, the Stream
- parameter is always a PAppStream, so a typecast can be used to find the
- corresponding ObjectWindows stream. }
-
- function StreamPut(Stream: POleStream; Buffer: PChar;
- Size: LongInt): LongInt; export;
- begin
- StreamPut := StreamInOut(PAppStream(Stream)^.OwlStream^,
- Buffer, Size, True);
- end;
-
- { TObjectWindow methods }
-
- { Construct an OLE object window. The AppClient structure is initialized
- to reference the newly created TObjectWindow so that the ClientCallBack
- routine can later locate it when notifications are received. If the OLE
- object is successfully created, its bounds are queried to determine the
- initial bounds of the OLE object window. Notice that the bounds are
- returned in mm_HiMetric units, which are converted to mm_Text units. }
-
- constructor TObjectWindow.Init(Link: Boolean);
- var
- R: TRect;
- begin
- TWindow.Init(MainWindow, nil);
- Attr.Style := ws_Child + ws_ClipSiblings;
- AppClient.OleClient.lpvtbl := @OleClientVTbl;
- AppClient.ObjectWindow := @Self;
- OleObject := nil;
- Framed := False;
- if Link then
- Check(OleCreateLinkFromClip(OleProtocol, @AppClient.OleClient,
- MainWindow^.ClientDoc, OleObjectName, OleObject,
- olerender_Draw, 0))
- else
- Check(OleCreateFromClip(OleProtocol, @AppClient.OleClient,
- MainWindow^.ClientDoc, OleObjectName, OleObject,
- olerender_Draw, 0));
- if OleObject = nil then Status := -1 else
- begin
- OleQueryBounds(OleObject, R);
- Attr.X := 0;
- Attr.Y := 0;
- Attr.W := MulDiv(R.right, PixPerInch.X, 2540);
- Attr.H := MulDiv(-R.bottom, PixPerInch.Y, 2540);
- end;
- end;
-
- { Load an OLE object window from a stream. Loads the contained OLE object
- from the stream, using a TAppStream for I/O. }
-
- constructor TObjectWindow.Load(var S: TStream);
- var
- ObjectType: Longint;
- AppStream: TAppStream;
- begin
- TWindow.Load(S);
- AppClient.OleClient.lpvtbl := @OleClientVTbl;
- AppClient.ObjectWindow := @Self;
- OleObject := nil;
- Framed := False;
- AppStream.OleStream.lpstbl := @OleStreamVTbl;
- AppStream.OwlStream := @S;
- Check(OleLoadFromStream(@AppStream.OleStream, OleProtocol,
- @AppClient.OleClient, MainWindow^.ClientDoc, OleObjectName,
- OleObject));
- if OleObject = nil then Status := -1;
- end;
-
- { Destroy an OLE object window. Closes and releases the contained OLE
- object. }
-
- destructor TObjectWindow.Done;
- begin
- if OleObject <> nil then
- begin
- CloseObject;
- Check(OleRelease(OleObject));
- end;
- TWindow.Done;
- end;
-
- { Return the OLE object window class name }
-
- function TObjectWindow.GetClassName: PChar;
- begin
- GetClassName := 'OleWindow';
- end;
-
- { Return the OLE object window class structure. Enables double click
- processing. }
-
- procedure TObjectWindow.GetWindowClass(var AWndClass: TWndClass);
- begin
- TWindow.GetWindowClass(AWndClass);
- AWndClass.Style := AWndClass.Style or cs_DblClks;
- end;
-
- { Initialize an OLE object window. Called following successful creation
- of the MS-Windows window. The window is brought to front and shown. }
-
- procedure TObjectWindow.SetupWindow;
- begin
- TWindow.SetupWindow;
- BringToFront;
- ShowWindow(HWindow, sw_Show);
- end;
-
- { Store an OLE object window on a stream. Stores the contained OLE object
- on the stream, using a TAppStream for I/O. }
-
- procedure TObjectWindow.Store(var S: TStream);
- var
- AppStream: TAppStream;
- begin
- TWindow.Store(S);
- AppStream.OleStream.lpstbl := @OleStreamVTbl;
- AppStream.OwlStream := @S;
- Check(OleSaveToStream(OleObject, @AppStream.OleStream));
- end;
-
- { Paint an OLE object window. The contained OLE object is instructed to
- draw itself to fill the entire client area. }
-
- procedure TObjectWindow.Paint(PaintDC: HDC; var PaintInfo: TPaintStruct);
- var
- R: TRect;
- begin
- GetClientRect(HWindow, R);
- Check(OleDraw(OleObject, PaintDC, R, R, 0));
- end;
-
- { Determine whether an OLE object window can close. If the contained OLE
- object is currently open, the user must confirm before the window can
- be closed. }
-
- function TObjectWindow.CanClose: Boolean;
- begin
- CanClose := True;
- if OleQueryOpen(OleObject) = ole_Ok then
- CanClose := Message('Object is currently open. Continue anyway?',
- mb_IconExclamation + mb_OkCancel) = id_Ok;
- end;
-
- { Check the status of an OLE operation. If an OLE operation returns
- ole_Wait_For_Release, indicating that it is executing acsynchronously,
- the Check method will enter a message loop, waiting for the OLE object
- to be released by the server. }
-
- procedure TObjectWindow.Check(OleStatus: TOleStatus);
- var
- M: TMsg;
- begin
- if OleStatus = ole_Wait_For_Release then
- begin
- repeat
- OleStatus := OleQueryReleaseStatus(OleObject);
- if OleStatus = ole_Busy then
- if GetMessage(M, 0, 0, 0) then
- begin
- TranslateMessage(M);
- DispatchMessage(M);
- end;
- until OleStatus <> ole_Busy;
- end;
- if OleStatus <> ole_Ok then OleError(OleStatus);
- end;
-
- { Return the class name of the contained OLE object. The first string in
- an OLE object's ObjectLink or OwnerLink data is the class name. }
-
- procedure TObjectWindow.GetObjectClass(ClassName: PChar);
- var
- H: THandle;
- begin
- ClassName[0] := #0;
- if (OleGetData(OleObject, CFObjectLink, H) = ole_Ok) or
- (OleGetData(OleObject, CFOwnerLink, H) = ole_Ok) then
- begin
- StrCopy(ClassName, GlobalLock(H));
- GlobalUnlock(H);
- end;
- end;
-
- { Return True if the contained OLE object is a linked object. }
-
- function TObjectWindow.IsLinked: Boolean;
- var
- ObjectType: Longint;
- begin
- IsLinked := (OleQueryType(OleObject, ObjectType) = ole_Ok) and
- (ObjectType = ot_Link);
- end;
-
- { Update the contained OLE object. }
-
- procedure TObjectWindow.Update;
- begin
- Check(OleUpdate(OleObject));
- end;
-
- { Open the contained OLE object. }
-
- procedure TObjectWindow.OpenObject(Verb: Word);
- begin
- Check(OleActivate(OleObject, Verb, True, True, 0, nil));
- end;
-
- { Close the contained OLE object if it is open. }
-
- procedure TObjectWindow.CloseObject;
- begin
- if OleQueryOpen(OleObject) = ole_Ok then Check(OleClose(OleObject));
- end;
-
- { Copy the contained OLE object to the clipboard. }
-
- procedure TObjectWindow.CopyToClipboard;
- begin
- Check(OleCopyToClipboard(OleObject));
- end;
-
- { Delete an OLE object window. If the window is the main window's
- current selection, it is unselected. The parent window is marked as
- modified, and the contained OLE object is closed and deleted. }
-
- procedure TObjectWindow.Delete;
- begin
- with MainWindow^ do
- begin
- if ObjectWindow = @Self then SelectWindow(nil);
- Modified := True;
- end;
- CloseObject;
- Check(OleDelete(OleObject));
- OleObject := nil;
- Free;
- end;
-
- { This method is called by the ClientCallBack routine whenever the
- contained OLE object has changed. The client area of the OLE object
- window is invalidated to force repainting, and the main window is
- marked as modified. }
-
- procedure TObjectWindow.Changed;
- begin
- InvalidateRect(HWindow, nil, True);
- MainWindow^.Modified := True;
- end;
-
- { Bring an OLE object window to front. }
-
- procedure TObjectWindow.BringToFront;
- begin
- SetWindowPos(HWindow, 0, 0, 0, 0, 0, swp_NoMove + swp_NoSize);
- end;
-
- { Return the bounds of an OLE object window using parent window
- coordinates. The bounds include the window frame, if present. }
-
- procedure TObjectWindow.GetBounds(var R: TRect);
- begin
- GetWindowRect(HWindow, R);
- ScreenToClient(Parent^.HWindow, PPoint(@R.left)^);
- ScreenToClient(Parent^.HWindow, PPoint(@R.right)^);
- end;
-
- { Set the bounds of an OLE object window within its parent window. }
-
- procedure TObjectWindow.SetBounds(var R: TRect);
- begin
- MoveWindow(HWindow, R.left, R.top,
- R.right - R.left, R.bottom - R.top, True);
- UpdateWindow(HWindow);
- end;
-
- { Enable or disable an OLE object window's window frame. The frame is
- added or removed by modifying the window's style flags and growing or
- shrinking the window's bounds. }
-
- procedure TObjectWindow.ShowFrame(EnableFrame: Boolean);
- const
- Border = ws_Border + ws_ThickFrame;
- var
- FX, FY: Integer;
- Style: Longint;
- R: TRect;
- begin
- if EnableFrame <> Framed then
- begin
- Style := GetWindowLong(HWindow, gwl_Style);
- FX := GetSystemMetrics(sm_CXFrame);
- FY := GetSystemMetrics(sm_CYFrame);
- GetBounds(R);
- if EnableFrame then
- begin
- Style := Style or Border;
- InflateRect(R, FX, FY);
- end else
- begin
- Style := Style and not Border;
- InflateRect(R, -FX, -FY);
- end;
- SetWindowLong(HWindow, gwl_Style, Style);
- SetBounds(R);
- Framed := EnableFrame;
- end;
- end;
-
- { wm_GetMinMaxInfo message handler. Modifies the minimum window size. }
-
- procedure TObjectWindow.WMGetMinMaxInfo(var Msg: TMessage);
- type
- PMinMaxInfo = ^TMinMaxInfo;
- TMinMaxInfo = array[0..4] of TPoint;
- begin
- PMinMaxInfo(Msg.LParam)^[3].X := 24;
- PMinMaxInfo(Msg.LParam)^[3].Y := 24;
- end;
-
- { wm_Move message handler. Updates the window location in the Attr field
- and marks the main window as modified. }
-
- procedure TObjectWindow.WMMove(var Msg: TMessage);
- begin
- if (Attr.X <> Msg.LParamLo) or (Attr.Y <> Msg.LParamHi) then
- begin
- Attr.X := Msg.LParamLo;
- Attr.Y := Msg.LParamHi;
- MainWindow^.Modified := True;
- end;
- end;
-
- { wm_Size message handler. Updates the window size in the Attr field and
- marks the main window as modified. }
-
- procedure TObjectWindow.WMSize(var Msg: TMessage);
- begin
- if (Attr.W <> Msg.LParamLo) or (Attr.H <> Msg.LParamHi) then
- begin
- Attr.W := Msg.LParamLo;
- Attr.H := Msg.LParamHi;
- MainWindow^.Modified := True;
- end;
- end;
-
- { wm_LButtonDown message handler. Brings the window to front and selects
- it, causing a frame to be drawn around the window. If a dragging
- operation is not in effect, one is initiated by capturing the mouse
- and recording the initial dragging location. }
-
- procedure TObjectWindow.WMLButtonDown(var Msg: TMessage);
- begin
- BringToFront;
- MainWindow^.SelectWindow(@Self);
- if not Dragging then
- begin
- Dragging := True;
- SetCapture(HWindow);
- DragPoint := TPoint(Msg.LParam);
- ClientToScreen(HWindow, DragPoint);
- end;
- end;
-
- { wm_MouseMove message handler. If a dragging operation is in effect,
- the window is moved and the client area of the parent window is
- repainted. }
-
- procedure TObjectWindow.WMMouseMove(var Msg: TMessage);
- var
- P: TPoint;
- R: TRect;
- begin
- if Dragging then
- begin
- P := TPoint(Msg.LParam);
- ClientToScreen(HWindow, P);
- GetBounds(R);
- OffsetRect(R, P.X - DragPoint.X, P.Y - DragPoint.Y);
- SetBounds(R);
- UpdateWindow(Parent^.HWindow);
- DragPoint := P;
- end;
- end;
-
- { wm_LButtonUp message handler. Terminates a dragging operation. }
-
- procedure TObjectWindow.WMLButtonUp(var Msg: TMessage);
- begin
- if Dragging then
- begin
- ReleaseCapture;
- Dragging := False;
- end;
- end;
-
- { wm_LButtonDblClk message handler. Opens the contained OLE object by
- executing its primary verb. This is typically an 'Edit' or 'Play'
- operation. }
-
- procedure TObjectWindow.WMLButtonDblClk(var Msg: TMessage);
- begin
- OpenObject(oleverb_Primary);
- end;
-
- { TMainWindow methods }
-
- { Construct the application's main window. Loads the main menu and
- creates an OLE document. }
-
- constructor TMainWindow.Init;
- var
- P: PObjectWindow;
- begin
- MainWindow := @Self;
- TWindow.Init(nil, nil);
- Attr.Menu := LoadMenu(HInstance, PChar(id_Menu));
- ObjectWindow := nil;
- SetFilename('');
- InitDocument;
- end;
-
- { Destroy the application's main window. Destroys the contained OLE
- document. }
-
- destructor TMainWindow.Done;
- begin
- DoneDocument;
- TWindow.Done;
- end;
-
- { Determine whether the main window can close. Checks whether the
- contained OLE object windows can close, and then prompts the user if
- any modifications have been made since the file was opened or saved. }
-
- function TMainWindow.CanClose: Boolean;
- begin
- CanClose := False;
- if TWindow.CanClose then
- begin
- CanClose := True;
- if Modified then
- case Message('Save current changes?',
- mb_IconExclamation + mb_YesNoCancel) of
- id_Yes: CanClose := Save;
- id_Cancel: CanClose := False;
- end;
- end;
- end;
-
- { Create the main window's OLE document. }
-
- procedure TMainWindow.InitDocument;
- var
- P: PChar;
- begin
- P := Filename;
- if P[0] = #0 then P := 'Untitled';
- OleRegisterClientDoc('OleClntDemo', P, 0, ClientDoc);
- Modified := False;
- end;
-
- { Destroy the main window's OLE document. The contained OLE object
- windows are destroyed before the document. }
-
- procedure TMainWindow.DoneDocument;
-
- procedure FreeObjectWindow(P: PObjectWindow); far;
- begin
- P^.Free;
- end;
-
- begin
- ForEach(@FreeObjectWindow);
- OleRevokeClientDoc(ClientDoc);
- end;
-
- { Update the main window's OLE document. Each object window is checked
- to see if it contains a linked OLE object, and if so, the user is given
- the option to update the link. }
-
- procedure TMainWindow.UpdateDocument;
- var
- Prompted, DoUpdate: Boolean;
-
- procedure UpdateObjectWindow(P: PObjectWindow); far;
- begin
- if P^.IsLinked then
- begin
- if not Prompted then
- begin
- DoUpdate := Message('This file contains linked objects.'#13 +
- 'Update links now?',
- mb_IconExclamation + mb_YesNo) = id_Yes;
- Prompted := True;
- end;
- if DoUpdate then P^.Update;
- end;
- end;
-
- begin
- Prompted := False;
- ForEach(@UpdateObjectWindow);
- end;
-
- { Set the name of the file in the main window. Updates the title of the
- main window to include the base part of the filename. }
-
- procedure TMainWindow.SetFilename(Name: PChar);
- var
- Params: array[0..1] of PChar;
- Title: array[0..63] of Char;
- begin
- StrCopy(Filename, Name);
- Params[0] := OleClntTitle;
- if Name[0] = #0 then Params[1] := '(Untitled)' else
- begin
- Params[1] := StrRScan(Name, '\');
- if Params[1] = nil then Params[1] := Name else Inc(Params[1]);
- end;
- wvsprintf(Title, '%s - %s', Params);
- if hWindow <> 0 then SetCaption(Title);
- end;
-
- { Load a file into the main window. If the file does not exist, a new
- file is created. Otherwise, the file header is checked, and the
- contained OLE object windows are read from the stream. }
-
- function TMainWindow.LoadFile: Boolean;
- var
- Header: TOleFileHeader;
- S: TBufStream;
- begin
- LoadFile := False;
- S.Init(Filename, stOpenRead, 4096);
- if S.Status = 0 then
- begin
- S.Read(Header, SizeOf(TOleFileHeader));
- if Longint(Header) = Longint(OleFileHeader) then
- begin
- GetChildren(S);
- if (S.Status = 0) and CreateChildren then
- begin
- UpdateDocument;
- LoadFile := True;
- end else
- Error('Error reading file %s.', Filename);
- end else
- Error('File format error %s.', Filename);
- end else
- LoadFile := True;
- S.Done;
- end;
-
- { Save the file in the main window. The OLE client library is notified if
- the file was successfully saved. }
-
- function TMainWindow.SaveFile: Boolean;
- var
- S: TBufStream;
- begin
- SaveFile := False;
- S.Init(Filename, stCreate, 4096);
- if S.Status = 0 then
- begin
- S.Write(OleFileHeader, SizeOf(TOleFileHeader));
- PutChildren(S);
- if S.Status = 0 then
- begin
- OleSavedClientDoc(ClientDoc);
- Modified := False;
- SaveFile := True;
- end else
- Error('Error writing file %s.', Filename);
- end else
- Error('Error creating file %s.', Filename);
- S.Done;
- end;
-
- { Open a new or existing file. The current OLE document is destroyed, a
- new document is created, and the file is loaded. }
-
- function TMainWindow.NewFile(Name: PChar): Boolean;
- begin
- DoneDocument;
- SetFilename(Name);
- InitDocument;
- if Filename[0] <> #0 then NewFile := LoadFile else NewFile := True;
- end;
-
- { Save the current file. If the file is untitled, prompt the user for a
- name. }
-
- function TMainWindow.Save: Boolean;
- begin
- if Filename[0] = #0 then Save := SaveAs else Save := SaveFile;
- end;
-
- { Save the current file under a new name. The OLE client library is
- informed that the document has been renamed. }
-
- function TMainWindow.SaveAs: Boolean;
- var
- Name: TFilename;
- begin
- SaveAs := False;
- StrCopy(Name, Filename);
- if FileDialog(HWindow, Name, True) then
- begin
- SetFilename(Name);
- OleRenameClientDoc(ClientDoc, Name);
- SaveAs := SaveFile;
- end;
- end;
-
- { Create a new OLE object window using data in the clipboard. The Link
- parameter determines whether to create an embedded object or a linked
- object. }
-
- procedure TMainWindow.NewObjectWindow(Link: Boolean);
- begin
- OpenClipboard(HWindow);
- SelectWindow(PObjectWindow(Application^.MakeWindow(
- New(PObjectWindow, Init(Link)))));
- CloseClipboard;
- end;
-
- { Select a given OLE object window. }
-
- procedure TMainWindow.SelectWindow(Window: PObjectWindow);
- begin
- if ObjectWindow <> Window then
- begin
- if ObjectWindow <> nil then ObjectWindow^.ShowFrame(False);
- ObjectWindow := Window;
- if ObjectWindow <> nil then ObjectWindow^.ShowFrame(True);
- end;
- end;
-
- { Update the Edit|Object menu. The Registration Database is queried to
- find the readable version of the class name of the current OLE object,
- along with the list of verbs supported by the class. If the class
- supports more than one verb, the verbs are put on a popup submenu. }
-
- procedure TMainWindow.UpdateObjectMenu;
- var
- VerbFound: Boolean;
- VerbCount: Word;
- EditMenu, PopupMenu: HMenu;
- Size: Longint;
- Params: array[0..1] of Pointer;
- ClassName, ClassText, Verb: array[0..31] of Char;
- Buffer: array[0..255] of Char;
- begin
- EditMenu := GetSubMenu(Attr.Menu, pos_Edit);
- DeleteMenu(EditMenu, pos_Object, mf_ByPosition);
- if ObjectWindow <> nil then
- begin
- ObjectWindow^.GetObjectClass(ClassName);
- if ClassName[0] <> #0 then
- begin
- Size := SizeOf(ClassText);
- if RegQueryValue(hkey_Classes_Root, ClassName,
- ClassText, Size) = 0 then
- begin
- PopupMenu := CreatePopupMenu;
- VerbCount := 0;
- repeat
- Params[0] := @ClassName;
- Params[1] := Pointer(VerbCount);
- wvsprintf(Buffer, '%s\protocol\StdFileEditing\verb\%d', Params);
- Size := SizeOf(Verb);
- VerbFound := RegQueryValue(hkey_Classes_Root,
- Buffer, Verb, Size) = 0;
- if VerbFound then
- begin
- InsertMenu(PopupMenu, VerbCount, mf_ByPosition,
- cm_VerbMin + VerbCount, Verb);
- Inc(VerbCount);
- end;
- until not VerbFound;
- if VerbCount <= 1 then
- begin
- if VerbCount = 0 then
- Params[0] := PChar('Edit') else
- Params[0] := @Verb;
- Params[1] := @ClassText;
- wvsprintf(Buffer, '%s %s &Object', Params);
- InsertMenu(EditMenu, pos_Object, mf_ByPosition,
- cm_VerbMin, Buffer);
- DestroyMenu(PopupMenu);
- end else
- begin
- Params[0] := @ClassText;
- wvsprintf(Buffer, '%s &Object', Params);
- InsertMenu(EditMenu, pos_Object, mf_ByPosition + mf_Popup,
- PopupMenu, Buffer);
- end;
- Exit;
- end;
- end;
- end;
- InsertMenu(EditMenu, pos_Object, mf_ByPosition + mf_Grayed,
- 0, '&Object');
- end;
-
- { wm_LButtonDown message handler. Deselects the current OLE object
- window. }
-
- procedure TMainWindow.WMLButtonDown(var Msg: TMessage);
- begin
- SelectWindow(nil);
- end;
-
- { wm_InitMenu message handler. Updates the Edit menu. }
-
- procedure TMainWindow.WMInitMenu(var Msg: TMessage);
- var
- HasSelection: Boolean;
-
- procedure SetMenuItem(Item: Word; Enable: Boolean);
- var
- Flags: Word;
- begin
- if Enable then Flags := mf_Enabled else Flags := mf_Grayed;
- EnableMenuItem(Attr.Menu, Item, Flags);
- end;
-
- begin
- HasSelection := ObjectWindow <> nil;
- SetMenuItem(cm_EditCut, HasSelection);
- SetMenuItem(cm_EditCopy, HasSelection);
- SetMenuItem(cm_EditClear, HasSelection);
- SetMenuItem(cm_EditPaste, OleQueryCreateFromClip(
- OleProtocol, olerender_Draw, 0) = ole_OK);
- SetMenuItem(cm_EditPasteLink, OleQueryLinkFromClip(
- OleProtocol, olerender_Draw, 0) = ole_OK);
- UpdateObjectMenu;
- end;
-
- { File|New command handler. Checks whether the current file can be
- closed, and creates a new untitled file if possible. }
-
- procedure TMainWindow.CMFileNew(var Msg: TMessage);
- begin
- if CanClose then NewFile('');
- end;
-
- { File|Open command handler. Checks whether the current file can be
- closed, and opens a new file if possible. }
-
- procedure TMainWindow.CMFileOpen(var Msg: TMessage);
- var
- Name: TFilename;
- begin
- if CanClose then
- begin
- Name[0] := #0;
- if FileDialog(HWindow, Name, False) then
- if not NewFile(Name) then NewFile('');
- end;
- end;
-
- { File|Save command handler. }
-
- procedure TMainWindow.CMFileSave(var Msg: TMessage);
- begin
- Save;
- end;
-
- { File|Save as command handler. }
-
- procedure TMainWindow.CMFileSaveAs(var Msg: TMessage);
- begin
- SaveAs;
- end;
-
- { File|Exit command handler. }
-
- procedure TMainWindow.CMFileExit(var Msg: TMessage);
- begin
- CloseWindow;
- end;
-
- { Edit|Cut command handler. Performs a Copy followed by a Clear. }
-
- procedure TMainWindow.CMEditCut(var Msg: TMessage);
- begin
- CMEditCopy(Msg);
- CMEditClear(Msg);
- end;
-
- { Edit|Copy command handler. If an OLE object window is currently
- selected, the clipboard is emptied, and the OLE object window is
- instructed to copy the contained OLE object to the clipboard. }
-
- procedure TMainWindow.CMEditCopy(var Msg: TMessage);
- begin
- if ObjectWindow <> nil then
- begin
- OpenClipBoard(HWindow);
- EmptyClipBoard;
- ObjectWindow^.CopyToClipboard;
- CloseClipBoard;
- end;
- end;
-
- { Edit|Paste command handler. Creates an embedded OLE object. }
-
- procedure TMainWindow.CMEditPaste(var Msg: TMessage);
- begin
- NewObjectWindow(False);
- end;
-
- { Edit|Paste link command handler. Creates a linked OLE object. }
-
- procedure TMainWindow.CMEditPasteLink(var Msg: TMessage);
- begin
- NewObjectWindow(True);
- end;
-
- { Edit|Clear command handler. Deletes the currently selected OLE object
- window, if possible. }
-
- procedure TMainWindow.CMEditClear(var Msg: TMessage);
- begin
- if ObjectWindow <> nil then
- if ObjectWindow^.CanClose then ObjectWindow^.Delete;
- end;
-
- { Help|About command handler. Brings up the About box. }
-
- procedure TMainWindow.CMHelpAbout(var Msg: TMessage);
- begin
- Application^.ExecDialog(New(PDialog, Init(@Self, PChar(id_About))));
- end;
-
- { Default command handler method. Called when no explicit command handler
- can be found. If the command is within the range reserved for OLE
- object verbs, the current OLE object window is instructed to execute
- the verb. }
-
- procedure TMainWindow.DefCommandProc(var Msg: TMessage);
- begin
- if (Msg.WParam >= cm_VerbMin) and (Msg.WParam <= cm_VerbMax) then
- begin
- if ObjectWindow <> nil then
- ObjectWindow^.OpenObject(Msg.WParam - cm_VerbMin);
- end else
- TWindow.DefCommandProc(Msg);
- end;
-
- { TApp methods }
-
- { Construct the application object. Queries the pixels-per-inch ratios
- of the display for later use in conversions between mm_HiMetric and
- mm_Text coordinates. Creates callback procedure instances for the OLE
- client and OLE stream virtual tables. Registers the OwnerLink and
- ObjectLink clipboard formats for later use in OleGetData calls.
- Registers TObjectWindow for stream I/O. }
-
- constructor TApp.Init(AName: PChar);
- var
- DC: HDC;
- begin
- TApplication.Init(AName);
- DC := GetDC(0);
- PixPerInch.X := GetDeviceCaps(DC, logPixelsX);
- PixPerInch.Y := GetDeviceCaps(DC, logPixelsY);
- ReleaseDC(0, DC);
- @OleClientVTbl.CallBack := MakeProcInstance(@ClientCallBack, HInstance);
- @OleStreamVTbl.Get := MakeProcInstance(@StreamGet, HInstance);
- @OleStreamVTbl.Put := MakeProcInstance(@StreamPut, HInstance);
- CFOwnerLink := RegisterClipboardFormat('OwnerLink');
- CFObjectLink := RegisterClipboardFormat('ObjectLink');
- RegisterType(RObjectWindow);
- end;
-
- { Destroy the application object. Frees the OLE client and OLE stream
- virtual table procedure instances. }
-
- destructor TApp.Done;
- begin
- FreeProcInstance(@OleClientVTbl.CallBack);
- FreeProcInstance(@OleStreamVTbl.Get);
- FreeProcInstance(@OleStreamVTbl.Put);
- TApplication.Done;
- end;
-
- { Create the main window. }
-
- procedure TApp.InitMainWindow;
- begin
- MainWindow := New(PMainWindow, Init);
- end;
-
- { Main program }
-
- begin
- App.Init('OleClntDemo');
- App.Run;
- App.Done;
- end.
-